package flca.mda.api.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import mda.annotation.DatabaseUnit;
import mda.annotation.PersistenceType;
import mda.annotation.crud.Search;
import mda.annotation.gui.Form;
import mda.type.IApplicationType;
import mda.type.IDtoType;
import mda.type.IEntityType;
import mda.type.IServiceType;
import snippets.backend.JavaDaoBind;
import snippets.backend.JavaRestServiceBind;
import snippets.backend.JavaServiceBind;
import webapp.Tid;
import flca.mda.codegen.data.ITemplate;
import flca.mda.codegen.helpers.StrUtil;
import flca.mda.common.api.helpers.ImportHelper;

public class WebappTypeUtils extends TypeUtils {


	/**
	 * This method returns bool to indicate if Maint & Detail forms should be
	 * generated. Ny default, Entities return true and Dto's return false, but
	 * this can be overriden by the annotation Form.generate()
	 * 
	 * @return
	 */
	public boolean generateForms() {
		Class<?> currclz = JetArgument.getCurrent().getElementClass();

		if (hasType(currclz, IDtoType.class)) {
			if (hasAnnotation(currclz, mda.annotation.gui.Form.class)) {
				return ((Form) getAnnotation(currclz, Form.class)).generate();
			} else {
				return false; // by default, forms are NOT generated for dto's
			}
		} else {
			if (hasAnnotation(currclz, mda.annotation.gui.Form.class)) {
				return ((Form) getAnnotation(currclz, Form.class)).generate();
			} else {
				return true; // by default, forms ARE generated for entities
			}
		}
	}

	/**
	 * This return the simple name of the generated Search dto class.
	 * Unfortunately we can not use the TemplateUtils because the target class
	 * is the entity
	 * 
	 * @param crudEntity
	 * @return
	 */
	public String getSearchDtoName(Class<?> crudEntity) {
		// hmm, can we improve this?
		String result = "";

		if (hasAnnotation(crudEntity, Search.class)) {
			result = "Search" + crudEntity.getSimpleName() + "Dto";
			String pck = StrUtil.getPackage(crudEntity.getName());
			ImportHelper.addImport(pck + "." + result);
		}

		return result;
	}

	@Override
	public String getAnnotations(Class<?> aClass) {
		return WebappAnnotationParser.parseAnnotations(aClass);
	}

	@Override
	public String getAnnotations(Field aField) {
		return WebappAnnotationParser.parseAnnotations(aField);
	}

	/**
	 * returns the database unit of the given IEntityType class
	 * 
	 * @return
	 */
	public String getDatabaseUnit(Class<?> aClass) {
		DatabaseUnit dbannot = (DatabaseUnit) getAnnotation(aClass,
				DatabaseUnit.class);
		if (dbannot != null) {
			return dbannot.name();
		} else {
			return "default";
		}
	}

	/**
	 * returns the database unit of the current IEntityType class
	 * 
	 * @return
	 */
	public String getDatabaseUnit() {
		return getDatabaseUnit(JetArgument.getCurrent().getElementClass());
	}

	/**
	 * returns the PersistenceType of the given IEntityType class
	 * 
	 * @return
	 */
	public PersistenceType getPersistenceType(Class<?> aClass) {
		DatabaseUnit dbannot = (DatabaseUnit) getAnnotation(aClass,
				DatabaseUnit.class);
		if (dbannot != null) {
			return dbannot.type();
		} else {
			return PersistenceType.JPA;
		}
	}

	/**
	 * returns the PersistenceType of the current IEntityType class
	 * 
	 * @return
	 */
	public PersistenceType getPersistenceType() {
		return getPersistenceType(JetArgument.getCurrent().getElementClass());
	}

	/**
	 * try to return dbunit given the method, by looking at the argument. The
	 * first argument with a valid DatabaseUnit annotation will be used,
	 * otherwise "default" will be returned
	 * 
	 * @param aMethod
	 * @return
	 */
	public String getDatabaseUnit(Method aMethod) {
		Class<?> params[] = aMethod.getParameterTypes();
		for (Class<?> clazz : params) {
			DatabaseUnit dbannot = (DatabaseUnit) getAnnotation(clazz,
					DatabaseUnit.class);
			if (dbannot != null) {
				return dbannot.name();
			}
		}
		return "default";
	}

	/**
	 * return all guice binding for the Entity XxxDao interfaces
	 * 
	 * @return
	 */
	public String getAllDaoBindings(String aBindProp) {
		StringBuffer sb = new StringBuffer();
		List<Class<?>> entities = ModelClassesUtils
				.findModelClassesWithInterface(IEntityType.class);
		String bindPropName = "b";
		for (Class<?> entity : entities) {
			JetArgument jetarg = new JetArgument(entity, null, null);
			sb.append(include(JavaDaoBind.class, jetarg, bindPropName) + "\n\n");
		}
		return sb.toString();
	}

	/**
	 * return all guice binding for the classes with IServiceType interfaces
	 * 
	 * @return
	 */
	public String getAllServiceBindings(String aBindProp) {
		StringBuffer sb = new StringBuffer();
		List<Class<?>> services = ModelClassesUtils
				.findModelClassesWithInterface(IServiceType.class);
		String bindPropName = "b";
		for (Class<?> srv : services) {
			JetArgument jetarg = new JetArgument(srv, null, null);
			sb.append(include(JavaServiceBind.class, jetarg, bindPropName)
					+ "\n\n");
		}
		return sb.toString();
	}

	/**
	 * return all guice binding for the classes with IServiceType interfaces
	 * 
	 * @return
	 */
	public String getAllRestServiceBindings(String aBindProp) {
		StringBuffer sb = new StringBuffer();
		List<Class<?>> services = ModelClassesUtils
				.findModelClassesWithInterface(IServiceType.class);
		String bindPropName = "b";
		for (Class<?> srv : services) {
			JetArgument jetarg = new JetArgument(srv, null, null);
			sb.append(include(JavaRestServiceBind.class, jetarg, bindPropName)
					+ "\n\n");
		}
		return sb.toString();
	}

	/**
	 * return all Rest implementations of all services that IServiceType
	 * interface
	 * 
	 * @param aTargetSetName
	 * @return
	 */
	public String getAllRestApps(String aTargetSetName) {
		StringBuffer sb = new StringBuffer();
		TemplateUtils tmpu = new TemplateUtils();
		List<Class<?>> services = ModelClassesUtils
				.findModelClassesWithInterface(IServiceType.class);
		Boolean doImport = true;
		for (Class<?> srv : services) {
			ITemplate t = tmpu.getTemplate(Tid.SERVICE_EXT_REST.name());
			String srvname = tmpu.getClassName(srv, t, doImport);
			sb.append("\t\t" + aTargetSetName + ".add(" + srvname
					+ ".class); \n");
		}
		return sb.toString();
	}

	/**
	 * return the one class that implements the IApplicationType interface
	 * 
	 * @return
	 */
	public IApplicationType getApplicationType() {
		Object obj = super.getApplicationBaseType();
		if (obj != null && obj instanceof IApplicationType) {
			return (IApplicationType) obj;
		} else {
			if (obj != null) {
				logger.error("You should have one class that extends ApplicationType (and not ApplicationBaseType");
			}
			return null;
		}
	}

}
